﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Moon.Sql
{
    internal class MethodCallExpressionProvider
    {
        private ExpressionProvide _expression = null;

        public MethodCallExpressionProvider(ExpressionProvide expression)
        {
            _expression = expression;
        }

        /// <summary>
        /// 表示对静态方法或实例方法的调用
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public string Resolve(Expression exp)
        {
            MethodCallExpression mce = exp as MethodCallExpression;
            string declaringType = mce.Method.DeclaringType.ToString();
            if (declaringType == "System.String")
            {
                //获取字段
                string left = _expression.Resolve(mce.Object ?? mce.Arguments[0]);
                List<object> resultList = _expression.GetMethodCallExpressionVal(mce);

                string result = "";
                switch (mce.Method.Name)
                {
                    #region string method
                    case "Contains":
                        //获取值
                        _expression.SetParameter("%" + _expression.GetValueType(resultList[0]) + "%");
                        result = string.Format("{0} like {1}", left, _expression.GetParamName());
                        break;
                    case "EndsWith":
                        _expression.SetParameter("%" + _expression.GetValueType(resultList[0]));
                        result = string.Format("{0} like {1}", left, _expression.GetParamName());
                        break;
                    case "StartsWith":
                        _expression.SetParameter(_expression.GetValueType(resultList[0]) + "%");
                        result = string.Format("{0} like {1}", left, _expression.GetParamName());
                        break;
                    case "IsNullOrEmpty":
                    case "IsNullOrWhiteSpace":
                        result = string.Format("({0} is null or {0} = '')", left);
                        break;
                    case "Concat":
                        if (mce.Arguments.Count == 1)
                        {
                            result = _expression.SqlDialect.Concat(left.Split(','));
                            break;
                        }
                        result = _expression.SqlDialect.Concat(left, _expression.GetParamVal(mce.Arguments[1]));
                        break;
                    case "IndexOf":
                        result = _expression.SqlDialect.IndexOf(left, _expression.GetParamVal(mce.Arguments[0]));
                        break;
                    case "PadLeft":
                        result = _expression.SqlDialect.PadLeft(left, _expression.GetParamVal(mce.Arguments[0]), _expression.GetParamVal(mce.Arguments[1]));
                        break;
                    case "PadRight":
                        result = _expression.SqlDialect.PadRight(left, _expression.GetParamVal(mce.Arguments[0]), _expression.GetParamVal(mce.Arguments[1]));
                        break;
                    case "Replace":
                        result = _expression.SqlDialect.Replace(left, $"'{resultList[0]}'", $"'{resultList[1]}'");
                        break;
                    case "Substring":
                        result = _expression.SqlDialect.Substring(left, resultList[0], resultList[1]);
                        break;
                    case "ToLower":
                        result = _expression.SqlDialect.ToLower(left);
                        break;
                    case "ToUpper":
                        result = _expression.SqlDialect.ToUpper(left);
                        break;
                    case "Trim":
                        result = _expression.SqlDialect.Trim(left);
                        break;
                    case "TrimEnd":
                        result = _expression.SqlDialect.TrimEnd(left);
                        break;
                    case "TrimStart":
                        result = _expression.SqlDialect.TrimStart(left);
                        break;
                    case "FirstOrDefault":
                        result = _expression.SqlDialect.FirstOrDefault(left);
                        break;
                    default:
                        throw new Exception("unsupported expression");
                        #endregion string method
                }
                return result;
            }
            else if (mce.Method.Name == "ToString" && mce.Method.ReturnType.FullName == "System.String")
            {
                //获取字段
                string left = _expression.Resolve(mce.Object ?? mce.Arguments[0]);
                return _expression.SqlDialect.ToString(left);
            }
            else if (declaringType == "System.Int32" || declaringType == "System.Int64")
            {
                //获取字段
                string left = _expression.Resolve(mce.Object ?? mce.Arguments[0]);
                switch (mce.Method.Name)
                {
                    case "ToString": return _expression.SqlDialect.ToString(left);
                    default:
                        throw new Exception("unsupported expression");
                }
            }
            else if (declaringType == "System.Convert")
            {
                string left = _expression.Resolve(mce.Object ?? mce.Arguments[0]);
                switch (mce.Method.Name)
                {
                    case "ToInt16":
                    case "ToInt32":
                    case "ToInt64":
                        return _expression.SqlDialect.ToInt(left);
                    default:
                        throw new Exception("unsupported expression");
                }
            }
            else if (declaringType == "System.Linq.Enumerable" || declaringType.Contains("List`1"))
            {
                //#2022.02.02
                if (mce.Arguments.Count >= 2)
                {
                    return $"{_expression.Resolve(mce.Arguments[1])} in ({_expression.Resolve(mce.Arguments[0])})";
                }
                return $"{_expression.Resolve(mce.Arguments[0])} in ({_expression.Resolve(mce.Object)})";
            }
            else if (declaringType == "System.Guid")
            {
                switch (mce.Method.Name)
                {
                    case "NewGuid": return _expression.SqlDialect.Guid;
                    default:
                        throw new Exception("unsupported expression");
                }
            }
            else if (declaringType == "System.DateTime")
            {
                //获取字段
                string left = _expression.Resolve(mce.Object ?? mce.Arguments[0]);
                switch (mce.Method.Name)
                {
                    case "Parse":
                        return _expression.SqlDialect.ToDateTime(_expression.GetParamVal(mce.Arguments[0]));
                    case "ToString":
                        return _expression.SqlDialect.ToDateFormat(left, _expression.GetParamVal(mce.Arguments[0]).Trim('\''));
                    default:
                        throw new Exception("unsupported expression");
                }
            }
            else if (declaringType == "System.Math")
            {
                switch (mce.Method.Name)
                {
                    case "Ceiling":
                        return _expression.SqlDialect.Ceiling(_expression.GetParamVal(mce.Arguments[0]));
                    case "Abs":
                        return _expression.SqlDialect.Abs(_expression.GetParamVal(mce.Arguments[0]));
                    default:
                        throw new Exception("unsupported expression");
                }
            }
            else if (declaringType == "Moon.Sql.DbFunc")
            {
                #region DbFunc
                if (mce.Method.Name.Contains("Datediff"))
                {
                    if (mce.Method.Name.IndexOf('_') != -1)
                    {
                        string dateType = mce.Method.Name.Substring(mce.Method.Name.IndexOf('_') + 1);
                        string left = _expression.Resolve(mce.Arguments[0]);
                        string right = _expression.Resolve(mce.Arguments[1]);
                        return _expression.SqlDialect.Datediff(dateType, left, right);
                    }
                }

                string result = "";
                string column = "";
                if (mce.Arguments.Count > 0)
                {
                    if (mce.Arguments[0].NodeType == ExpressionType.Constant)
                    {
                        column = (mce.Arguments[0] as ConstantExpression).Value.ToString();
                    }
                    else
                    {
                        column = _expression.Resolve(mce.Arguments[0]);
                    }
                }
                object val1 = "";
                switch (mce.Method.Name)
                {
                    case "Count":
                        result = $"count({column})";
                        break;
                    case "DistinctCount":
                        //#2022.11.20
                        result = $"count(distinct {column})";
                        break;
                    case "Sum":
                        result = $"sum({column})";
                        break;
                    case "Max":
                        result = $"max({column})";
                        break;
                    case "Min":
                        result = $"min({column})";
                        break;
                    case "Avg":
                        result = $"avg({column})";
                        break;
                    case "IsNull":
                        result = _expression.SqlDialect.IsNull(column, _expression.GetParamVal(mce.Arguments[1]));
                        break;
                    case "Sequence":
                        result = _expression.GetParamVal(mce.Arguments[0]);
                        break;
                    case "Ceiling":
                        result = _expression.SqlDialect.Ceiling(column);
                        break;
                    case "Custom":
                        if ((mce.Arguments[0] is ConstantExpression))
                        {
                            result = (mce.Arguments[0] as ConstantExpression).Value.ToString();
                        }
                        else
                        {
                            result = column;
                        }
                        break;
                    case "PartitionBy":
                        val1 = _expression.GetParamVal(mce.Arguments[2]);
                        Moon.Sql.OrderBy orderBy = (Moon.Sql.OrderBy)Enum.Parse(typeof(OrderBy), val1.ToString());
                        result = _expression.SqlDialect.PartitionBy(_expression.Resolve(mce.Arguments[0]), _expression.Resolve(mce.Arguments[1]), orderBy);
                        break;
                    case "ToDateFormat":
                        val1 = _expression.GetParamVal(mce.Arguments[1]).Trim('\'');
                        result = _expression.SqlDialect.ToDateFormat(column, val1?.ToString());
                        break;
                }
                return result;
                #endregion DbFunc
            }
            else if (declaringType.StartsWith("Moon.Sql.IQueryable"))
            {
                //子查询
                var sql = new SubqueryExpressionProvider(_expression).Resolve(mce);
                return sql;
            }
            
            throw new Exception("unsupported expression");
        }
    }
}
